import patchVnode from "./patchVNode";
import createElement from "./createElement";

export default function updateChildren(parentElm, oldCh, newCh) {
    // 旧前
    let oldStartIndex = 0;
    // 新前
    let newStartIndex = 0;
    // 旧后
    let oldEndIndex = oldCh.length - 1;
    // 新后
    let newEndIndex = newCh.length - 1;
    // 旧前节点
    let oldStartVnode = oldCh[0]
    // 旧后节点
    let oldEndVnode = oldCh[oldEndIndex];
    // 新前节点
    let newStartVnode = newCh[0]
    // 新后节点
    let newEndVnode = newCh[newEndIndex]

    let keyMap = null;

    while (oldStartIndex <= oldEndIndex && newStartIndex < newEndIndex) {
        // 首先判断加过undefined标记的节点
        if (oldStartVnode == null) {
            oldStartVnode = oldCh[++oldStartIndex];
        } else if (oldEndVnode == null) {
            oldEndVnode = oldCh[--oldEndIndex]
        } else if (newStartVnode == undefined) {
            newStartVnode = newCh[++newStartIndex]
        } else if (newEndVnode == undefined) {
            newEndVnode = newCh[--newEndIndex];
        } else if (checkIsSameVnode(oldStartIndex, newStartVnode)) {
            // 新前和旧前
            patchVnode(oldStartVnode, newStartVnode)
            oldStartVnode = oldCh[++oldStartIndex];
            newStartVnode = newCh[++newStartIndex];
        } else if (checkIsSameVnode(oldEndVnode, newEndVnode)) {
            // 新后和旧后
            patchVnode(oldEndVnode, newEndVnode);
            oldEndVnode = oldCh[--oldEndIndex];
            newEndVnode = newCh[--newEndIndex];
        } else if (checkIsSameVnode(newEndVnode, oldStartVnode)) {
            // 新后和旧前
            patchVnode(oldStartVnode, newEndVnode);
            // 当新后和旧前命中时，要移动节点。移动新前指向的这个节点到老节点的旧后的后面
            parentElm.insertBefore(oldStartVnode.elm, oldEndVnode.elm.nextSibling);
            newEndVnode = newCh[--newEndIndex];
            oldStartVnode = oldCh[++oldStartIndex];
        } else if (checkIsSameVnode(newStartVnode, oldEndVnode)) {
            // 新前和旧后
            patchVnode(oldEndVnode, newStartVnode)
            // 当新前和旧后命中时，要移动节点。移动新前指向的这个节点到老节点的旧前的前面
            parentElm.insertBefore(oldEndVnode.elm, oldStartVnode.elm);
            newStartVnode = newCh[++newStartIndex];
            oldEndVnode = oldCh[--oldEndIndex];
        } else {
            // 四种都没有命中
            // 寻找key的map
            if (!keyMap) {
                keyMap = {};
                // 从oldStartIndex开始，到oldEndIndex结束，创建keyMap映射对象
                for (let i = oldStartIndex; i < oldEndIndex; i++) {
                    const key = oldCh[i].key;
                    if (key != undefined) {
                        keyMap[key] = [i];
                    }
                }
            }
            // 寻找当前这项（newStartIndex）这项在keyMap中的映射的位置序号
            const indexOId = keyMap[newStartVnode.key];
            if (indexOId == undefined) {
                // 如果是undefined，表示是全新的值
                // 被加入的项（就是newStartVnode）现在不是真正的DOM节点
                parentElm.insertBefore(createElement((newStartVnode), oldStartVnode.elm))
            } else {
                // 如果不是undefined，就表示节点存在，不是新的节点，需要移动
                const elmToMove = oldCh[indexOId];
                if (elmToMove.elm.nodeType == 1) {
                    patchVnode(elmToMove, newStartVnode);
                    // 设置为undefined，表示已经处理完了
                    oldCh[indexOId] = undefined;
                    // 移动、调用insertBefore也可以实现
                    parentElm.insertBefore(elmToMove.elm, oldStartVnode.elm);
                }
            }
            newStartVnode = newCh[++newStartIndex];
        }
    }
    // 判断有没有剩余节点未处理，循环结束后，前是否比后小
    if (newStartIndex <= newEndIndex) {
        // 有剩余节点还没有处理，要加项，要把所有剩余的节点，都插入到oldStartIndex之前
        // 插入的标杆
        // const before = newCh[newEndIndex + 1] == null ? null : newCh[newEndIndex + 1].elm;
        // 遍历新的newCh，添加到老的没有处理的之前
        for (let i = newStartIndex; i < newEndIndex; i++) {
            // insertBefore方法可以自动识别null，如果是null就会自动排到队尾去，和appendChild是一致了
            // newCh[i]还不是真正的DOM，所以要调用createElement()变为DOM
            // parentElm.insertBefore(createElement(newCh[i], before));
            parentElm.insertBefore(createElement(newCh[i], oldCh[oldStartIndex].elm));
        }
    } else if (oldStartIndex <= oldEndIndex) {
        // old还有剩余节点未处理，要删项
        // 批量删除oldStart和oldEnd指针之间的项
        for (let i = oldStartIndex; i < oldEndIndex; i++) {
            if (oldCh[i]) parentElm.removeChild(oldCh[i]);
        }
    }
}

// 判断是否是通一个虚拟节点
function checkIsSameVnode(a, b) {
    return a.sel === b.sel && a.key === b.key;
}
